#include "util/task_threads.h"

qcutil::CTasksThread::CTasksThread() :
    m_started_cb(nullptr)
  , m_terminated_cb(nullptr)
  , m_hEvent(NULL)
{
    m_bTerminte = true;
}

qcutil::CTasksThread::~CTasksThread()
{
    //printf("~thread %p\n", this);
}

void qcutil::CTasksThread::start(qcutil::CTasksThread::callback_t started_cb, qcutil::CTasksThread::callback_t terminated_cb)
{
    m_started_cb = started_cb;
    m_terminated_cb = terminated_cb;
    m_bTerminte = false;
    m_hEvent = qcutil::CreateEvent(true, false);
    m_thread.create(&CTasksThread::myThread, this);
    if (nullptr != m_started_cb) {
        invokeSync(m_started_cb);
    }
}

void qcutil::CTasksThread::stop(unsigned int forceKillTimeout)
{
    m_bTerminte = true;
    qcutil::SetEvent(m_hEvent);
    if (forceKillTimeout != (unsigned int)(-1))
        m_thread.waitToEndKill(forceKillTimeout);
    else
        m_thread.join();

    m_thread.close();
    if (m_hEvent)
    {
        qcutil::DestroyEvent(m_hEvent);
        m_hEvent = NULL;
    }
}

void qcutil::CTasksThread::invokeAsync(qcutil::CTasksThread::callback_t cb)
{
    std::lock_guard<std::recursive_mutex>lk(m_tasks_list_mutex);
    m_callback_list.push_back(cb);
    if (m_callback_list.size() == 1)
    {
        qcutil::SetEvent(m_hEvent);
    }
}

void qcutil::CTasksThread::invokeSync(qcutil::CTasksThread::callback_t cb)
{
    if (m_thread.isCurrentThread())
    {
        cb();
        return;
    }

    neosmart_event_t hEvent = qcutil::CreateEvent(true, false);
    {
        std::lock_guard<std::recursive_mutex> lk(m_tasks_list_mutex);
        m_callback_list.push_back([&]
        {
            try {
                cb();
            }
            catch (...) {

            }
            qcutil::SetEvent(hEvent);
        });
        if (m_callback_list.size() == 1) {
            qcutil::SetEvent(m_hEvent);
        }
    }
    qcutil::WaitForEvent(hEvent, WAIT_INFINITE);
    qcutil::DestroyEvent(hEvent);
    return;
}

bool qcutil::CTasksThread::isCurrentThread()
{
    return m_thread.isCurrentThread();
}

bool qcutil::CTasksThread::isRunning() { return !m_bTerminte; }

void *qcutil::CTasksThread::myThread(void *thread_param)
{
    CTasksThread* lpThis = (CTasksThread*)thread_param;
    while (!lpThis->m_bTerminte)
    {
        int dwWaitResult = qcutil::WaitForEvent(lpThis->m_hEvent, WAIT_INFINITE);
        if (!lpThis->m_bTerminte && WAIT_OBJECT_0 == dwWaitResult)
        {
            lpThis->processCallbackList();
        }
    }

    if (lpThis->m_terminated_cb != nullptr)
    {
        lpThis->m_terminated_cb();
    }
    return 0;
}

void qcutil::CTasksThread::processCallbackList()
{
    while (true)
    {
        callback_t cb = nullptr;
        {
            std::lock_guard<std::recursive_mutex> lk(m_tasks_list_mutex);
            if (m_callback_list.size() > 0)
            {
                cb = m_callback_list.front();
                m_callback_list.pop_front();
            }
            else
            {
                qcutil::ResetEvent(m_hEvent);
                return;
            }
        }
        if (cb)
        {
            cb();
        }
    }
}

